﻿#region Using

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using XNgine.Services;

#endregion

namespace XNgine.Input
{
    /// <summary>
    /// An input device that keeps track of the Mouse and MouseState
    /// </summary>
    public class MouseDevice : InputDevice<MouseState>, IMouseDevice
    {
        private MouseState _mouseState;

        public override MouseState State
        {
            get { return _mouseState; }
        }

        private Point lastMousePosition;
        private Point currentMousePosition;
        private int lastScrollPosition;

        private readonly Dictionary<MouseButton, PressedState> buttonStates =
            new Dictionary<MouseButton, PressedState>();

        public event MouseClickedHandler MouseClicked;
        public event MouseHeldHandler MouseHeld;
        public event MouseReleasedHandler MouseReleased;
        public event MouseMovedHandler MouseMoved;
        public event MouseScrolledHandler MouseScrolled;

        private bool _freezeMouse;

        public bool Freeze
        {
            get { return _freezeMouse; }
            set
            {
                _freezeMouse = value;
                if (_freezeMouse)
                {
                    _mouseState = Mouse.GetState();
                }
                lastMousePosition = new Point(_mouseState.X, _mouseState.Y);
            }
        }

        public override void Initialize()
        {
            base.Initialize();

            _mouseState = Mouse.GetState();
            lastMousePosition = new Point(_mouseState.X, _mouseState.Y);
            lastScrollPosition = _mouseState.ScrollWheelValue;

            foreach (MouseButton button in Enum.GetValues(typeof (MouseButton)))
            {
                buttonStates.Add(button, PressedState.Idle);
            }
        }

        public override void Update()
        {
            _mouseState = Mouse.GetState();
            currentMousePosition = new Point(_mouseState.X, _mouseState.Y);

            RaiseScrollEvents();
            RaiseMouseMoveEvents();

            RaiseButtonEvents();
        }

        private void RaiseButtonEvents()
        {
            // Mouse Buttons Checks
            Collection<MouseButton> pressed = new Collection<MouseButton>();
            Collection<MouseButton> held = new Collection<MouseButton>();
            Collection<MouseButton> released = new Collection<MouseButton>();

            Dictionary<MouseButton, ButtonState> pressedStates = GetButtonStates(_mouseState);
            foreach (MouseButton button in Enum.GetValues(typeof (MouseButton)))
            {
                buttonStates[button] = CheckPressedState(pressedStates[button], buttonStates[button]);

                if (buttonStates[button] == PressedState.Pressed)
                    pressed.Add(button);
                else if (buttonStates[button] == PressedState.Held)
                    held.Add(button);
                else if (buttonStates[button] == PressedState.Released)
                    released.Add(button);
            }

            // Event Calls
            if (pressed.Count > 0 && null != MouseClicked)
                MouseClicked(currentMousePosition, pressed);
            if (held.Count > 0 && null != MouseHeld)
                MouseHeld(currentMousePosition, held);
            if (released.Count > 0 && null != MouseReleased)
                MouseReleased(currentMousePosition, released);
        }

        private void RaiseMouseMoveEvents()
        {
            // Mouse Move Checks
            Vector2 mouseMoved = new Vector2(
                currentMousePosition.X - lastMousePosition.X,
                currentMousePosition.Y - lastMousePosition.Y);
            if (_freezeMouse)
                Mouse.SetPosition(lastMousePosition.X, lastMousePosition.Y);
            else
                lastMousePosition = currentMousePosition;
            if (mouseMoved.Length() > 0 && null != MouseMoved)
            {
                MouseMoved(mouseMoved);
            }
        }

        private void RaiseScrollEvents()
        {
            // Mouse Wheel Checks
            int scrollWheelValue = _mouseState.ScrollWheelValue;
            int scrollMoved = lastScrollPosition - scrollWheelValue;
            lastScrollPosition = scrollWheelValue;
            if (scrollMoved != 0 && null != MouseScrolled)
            {
                MouseScrolled(currentMousePosition, scrollMoved);
            }
        }

        private static Dictionary<MouseButton, ButtonState> GetButtonStates(MouseState state)
        {
            Dictionary<MouseButton, ButtonState> states =
                new Dictionary<MouseButton, ButtonState>
                    {
                        {MouseButton.Left, state.LeftButton},
                        {MouseButton.Right, state.RightButton},
                        {MouseButton.Middle, state.MiddleButton},
                        {MouseButton.X1, state.XButton1},
                        {MouseButton.X2, state.XButton2}
                    };

            return states;
        }
    }
}